package com.ttg.web.service.Impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.handler.inter.IWriter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ttg.common.entities.BasePage;
import com.ttg.common.entities.BaseResult;
import com.ttg.common.enums.*;
import com.ttg.common.exception.BaseException;
import com.ttg.common.utils.DateTimeUtil;
import com.ttg.common.utils.UserUtil;
import com.ttg.model.PeopleDto;
import com.ttg.model.dto.PeopleBindDto;
import com.ttg.model.dto.PeopleHistoryDto;
import com.ttg.model.dto.PeopleInfoDto;
import com.ttg.model.dto.PeopleInfoGetListDto;
import com.ttg.model.pojo.*;
import com.ttg.model.vo.*;
import com.ttg.web.dao.PeopleHistoryDao;
import com.ttg.web.dao.PeopleInfoDao;
import com.ttg.web.dao.TagInfoDao;
import com.ttg.web.dao.VipBindTagDao;
import com.ttg.web.redis.RedissonUtils;
import com.ttg.web.service.ManagePeopleInfoService;
import com.ttg.web.service.PeopleInfoService;
import com.ttg.web.service.TagInfoService;
import com.ttg.web.utils.NextUpdateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PeopleInfoServiceImpl implements PeopleInfoService {

    @Autowired
    private PeopleInfoDao peopleInfoDao;

    @Autowired
    private RedissonUtils redissonUtils;

    @Autowired
    private ManagePeopleInfoService managePeopleInfoService;

    @Autowired
    private VipBindTagDao vipBindTagDao;

    @Autowired
    private PeopleHistoryDao peopleHistoryDao;

    @Autowired
    private TagInfoDao tagInfoDao;

    @Override
    public BaseResult ProjectNum(PeopleInfoDto peopleInfoDto) {
        List<String> containsTagIds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(peopleInfoDto.getContainsTagIds())) {
            containsTagIds = peopleInfoDto.getContainsTagIds().stream().map(String::valueOf).collect(Collectors.toList());

        }
        List<String> excludeTagIds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(peopleInfoDto.getExcludeTagIds())) {
            excludeTagIds = peopleInfoDto.getExcludeTagIds().stream().map(String::valueOf).collect(Collectors.toList());
        }
        List<VipBindTag> vipBindTagList = vipBindTagDao.selectBytags(containsTagIds, excludeTagIds);
        return BaseResult.success(vipBindTagList.size());
    }

    @Override
    public BaseResult add(PeopleInfoDto dto) {
        if (ObjectUtil.isEmpty(dto)) {
            throw new BaseException(ErrorCodeEnums.PARAM_IS_NULL);
        }
        paramValidation(dto);
        PeopleInfo peopleInfo = new PeopleInfo();
        BeanUtil.copyProperties(dto, peopleInfo);
        peopleInfo.setAddTime(LocalDateTime.now());
        peopleInfo.setUpdateTime(LocalDateTime.now());
        peopleInfo.setAddId(UserUtil.getUserThrow().getAdmId());
        if (StringUtils.isBlank(peopleInfo.getCreateType())) {
            peopleInfo.setCreateType("customize");
        }
        peopleInfo.setVersionStatus(3);
        //计算标签执行时间并保存
        LocalDateTime nextUpdateTime = NextUpdateTimeUtil.firstUpdateTime(peopleInfo);
        peopleInfo.setPepNextUpdateTime(nextUpdateTime);

        //处理标签组
        if (!CollectionUtils.isEmpty(dto.getContainsTagIds())){
            String containsTagIds = dto.getContainsTagIds().stream().map(String::valueOf).collect(Collectors.joining(","));
            peopleInfo.setContainsTagIds(containsTagIds);
        }
        if (!CollectionUtils.isEmpty(dto.getExcludeTagIds())){
            String excludeTagIds = dto.getExcludeTagIds().stream().map(String::valueOf).collect(Collectors.joining(","));
            peopleInfo.setExcludeTagIds(excludeTagIds);
        }
        peopleInfo.setPepStatus(0);
        peopleInfoDao.save(peopleInfo);
        return BaseResult.success("SUCCESS");

    }

    @Override
    public BaseResult getList(PeopleInfoGetListDto dto) {
        if (dto.getPepStatus() == null) {
            dto.setPepStatus(0);
        }
        BasePage<Object> parse = dto.parse();
        BasePage<PeopleInfoGetListVo> voBasePage = peopleInfoDao.getList(dto);
        return BaseResult.success(voBasePage);
    }

    @Override
    public BaseResult delete(PeopleDto dto) {
        PeopleInfo peopleInfo = peopleInfoDao.getById(dto.getId());
        if (peopleInfo == null) {
            throw new RuntimeException("人群不存在");
        }
        peopleInfo.setPepStatus(2);
        boolean update = peopleInfoDao.updateById(peopleInfo);
        if (update) {
            return BaseResult.success("SUCCESS");
        } else {
            return BaseResult.defaultFail();
        }
    }

    @Override
    public BaseResult getOne(PeopleDto dto) {
        PeopleInfo peopleInfo = peopleInfoDao.getById(dto.getId());
        if (peopleInfo == null) {
            throw new RuntimeException("人群不存在");
        }
        PeopleOneVo vo = new PeopleOneVo();
        BeanUtils.copyProperties(peopleInfo, vo);
        //处理包含标签
        if (StringUtils.isNotBlank(peopleInfo.getContainsTagIds())) {
            List<Integer> containsTagIds = Arrays.stream(peopleInfo.getContainsTagIds().split(","))
                    .map(String::trim)
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());
            List<TagInfo> containsTags = tagInfoDao.selectByIds(containsTagIds);
            List<VipTag> containsTagsVo = new ArrayList<>();
            if (!CollectionUtils.isEmpty(containsTags)) {
                containsTagsVo = containsTags.stream().map(tagInfo -> {
                    VipTag vipTag = new VipTag();
                    BeanUtils.copyProperties(tagInfo, vipTag);
                    return vipTag;
                }).collect(Collectors.toList());
            }
            vo.setContainsTagIds(containsTagsVo);
        }
        //处理排除标签
        if (StringUtils.isNotBlank(peopleInfo.getExcludeTagIds())) {
            List<Integer>excludeTagIds = Arrays.stream(peopleInfo.getExcludeTagIds().split(","))
                    .map(String::trim)
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());
            List<TagInfo> excludeTags = tagInfoDao.selectByIds(excludeTagIds);
            List<VipTag> excludeTagVo = new ArrayList<>();
            if (!CollectionUtils.isEmpty(excludeTags)){
                excludeTagVo = excludeTags.stream().map(tagInfo -> {
                    VipTag vipTag = new VipTag();
                    BeanUtils.copyProperties(tagInfo, vipTag);
                    return vipTag;
                }).collect(Collectors.toList());
            }
            vo.setExcludeTagIds(excludeTagVo);
        }
        return BaseResult.success(vo);
    }

    @Override
    public BaseResult update(PeopleInfoDto dto) {
        if (ObjectUtil.isEmpty(dto) || ObjectUtil.isEmpty(dto.getId())) {
            throw new BaseException(ErrorCodeEnums.PARAM_IS_NULL);
        }
        PeopleInfo peopleInfo = new PeopleInfo();
        BeanUtil.copyProperties(dto, peopleInfo);
        peopleInfo.setId(dto.getId());
        peopleInfo.setUpdateTime(LocalDateTime.now());
        paramValidation(dto);
        //计算标签执行时间并保存
        LocalDateTime nextUpdateTime = NextUpdateTimeUtil.firstUpdateTime(peopleInfo);
        peopleInfo.setPepNextUpdateTime(nextUpdateTime);
        //处理标签组
        if (!CollectionUtils.isEmpty(dto.getContainsTagIds())){
            String containsTagIds = dto.getContainsTagIds().stream().map(String::valueOf).collect(Collectors.joining(","));
            peopleInfo.setContainsTagIds(containsTagIds);
        }
        if (!CollectionUtils.isEmpty(dto.getExcludeTagIds())){
            String excludeTagIds = dto.getExcludeTagIds().stream().map(String::valueOf).collect(Collectors.joining(","));
            peopleInfo.setExcludeTagIds(excludeTagIds);
        }
        peopleInfoDao.updateById(peopleInfo);
        return BaseResult.success("SUCCESS");
    }

    @Override
    public BaseResult peopleBind(PeopleBindDto reqDto) {

        if (Objects.isNull(reqDto) || Objects.isNull(reqDto.getId())) {
            throw new BaseException(ErrorCodeEnums.PARAM_IS_NULL);
        }
        PeopleInfo byId = peopleInfoDao.getById(reqDto.getId());
        if (Objects.isNull(byId)) {
            throw new BaseException(ErrorCodeEnums.TAG_NOT_EXISTS);
        }
        //计算下次更新时间
        LocalDateTime nextUpdateTime = NextUpdateTimeUtil.firstUpdateTime(byId);
        RLock rLock = null;
        try {
            rLock = redissonUtils.getRLock("PEOPLE" + byId.getId());
            boolean redisFlag = rLock.tryLock(5, 60 * 5, TimeUnit.SECONDS);
            if (!redisFlag) {
                throw new BaseException(ErrorCodeEnums.TAG_BIND_UPDATING.getErrcode(), ErrorCodeEnums.TAG_BIND_UPDATING.getMsg());
            }
            if (TagDataSourceEnum.USER.getValue().equals(byId.getPepDataSource())) {
                managePeopleInfoService.asyncUpdateVipPeople(byId, nextUpdateTime,byId.getPepNextUpdateTime());
            } else if (TagDataSourceEnum.MERCH.getValue().equals(byId.getPepDataSource())) {
                managePeopleInfoService.asyncUpdateVipPeople(byId, nextUpdateTime,byId.getPepNextUpdateTime());
            } else {
                throw new BaseException(ErrorCodeEnums.TAG_TYPE_NOT_EXISTS);
            }
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception e) {
            throw new BaseException("更新人群异常", e.getLocalizedMessage());
        } finally {
            if (Objects.nonNull(rLock) && rLock.isLocked()) {
                rLock.unlock();
            }
        }
        return BaseResult.success("SUCCESS");
    }

    @Override
    public BaseResult getHistoryList(PeopleHistoryDto reqDto) {
        if (reqDto.getPepId() != null) {
            PeopleInfo byId = peopleInfoDao.getById(reqDto.getPepId());
            if (Objects.isNull(byId)) {
                throw new BaseException(ErrorCodeEnums.TAG_NOT_EXISTS);
            }
        }
        BasePage<PeopleHistoryVo> parse = reqDto.parse();
        BasePage<PeopleHistoryVo> page = new BasePage<>(parse.getCurrent(), parse.getSize(), parse.getTotal(), parse.isSearchCount());
        BasePage<PeopleHistoryVo> voBasePage = peopleHistoryDao.getHistoryList(page, reqDto.getPepId(), reqDto.getHisStatus(), reqDto.getStartTime(), reqDto.getEndTime());
        return BaseResult.success(voBasePage);
    }

    @Override
    public void export(PeopleInfoGetListDto reqDto, HttpServletResponse response) {
        String fileName = "peopleExport";
        String fileType = ".xlsx";
        int extCount = 0;
        reqDto.setPage(1);
        reqDto.setPageSize(1000);
        BasePage<PeopleInfoGetListVo> basePage =  peopleInfoDao.getList(reqDto);
        List<PeopleInfoGetListVo> list = basePage.getList();
        List<PeopleInfoGetListVo> extList = new ArrayList<>();
        ExportParams params = new ExportParams(fileName + "导出时间：" + DateTimeUtil.formatYYYY_MM_DD_HH_MM_SS(LocalDateTime.now()) + "", "标签列表");
        params.setCreateHeadRows(true);
        IWriter<Workbook> workbookIWriter = ExcelExportUtil.exportBigExcel(params, PeopleInfoGetListVo.class);
        long total = basePage.getTotal();
        for (int i = 1; i <= 100; i++) {
            if (i != 1) {
                reqDto.setPage(i);
                basePage = peopleInfoDao.getList(reqDto);
            }
            list = basePage.getList();
            extCount += org.springframework.util.CollectionUtils.isEmpty(list) ? 0 : list.size();
            for (PeopleInfoGetListVo tempVo : list) {
                extList.add(tempVo);
            }
            list.clear();
            if (extCount >= total) {
                break;
            }
        }
        workbookIWriter.write(extList);
        Workbook workbook = workbookIWriter.get();
        if (workbook != null) {
            try (ServletOutputStream outputStream = response.getOutputStream()) {
                response.setCharacterEncoding("UTF-8");
                response.setHeader("content-Type", "application/vnd.ms-excel");
                response.setHeader("Content-Disposition", "attachment;filename=" + java.net.URLEncoder.encode(fileName + fileType, "UTF-8"));
                response.setHeader("Access-Control-Expose-Headers", "filename");
                response.setHeader("filename", java.net.URLEncoder.encode(fileName + fileType, "UTF-8"));
                workbook.write(outputStream);
            } catch (IOException e) {
                log.error("export people 导出异常", e);
                throw new BaseException(ErrorCodeEnums.EXPORT_IS_ERROR);
            }
        }
    }


    private void paramValidation(PeopleInfoDto dto) {
        if (dto == null) {
            throw new BaseException("参数为空");
        }
        if (!check(dto.getPepName())) {
            throw new BaseException("人群名称错误");
        }
        if (!checkEng(dto.getPepEnName())) {
            throw new BaseException("英文名称错误");
        }

        if (StringUtils.isBlank(dto.getUpdateFrequency())) {
            throw new BaseException("更新频率类型不能为空");
        }
        if ((dto.getUpdateFrequency().equals(TagUpdateFrequencyEnum.WEEKLY.getValue())
                || dto.getUpdateFrequency().equals(TagUpdateFrequencyEnum.MONTHLY.getValue()))) {
            if (StringUtils.isBlank(dto.getPepUpdateDates())) {
                throw new BaseException("更新频率不能为空");
            }
        }
        if (Objects.isNull(dto.getPepUpdateTime())) {
            throw new BaseException("更新时间不能为空");
        }
        if (CollectionUtils.isEmpty(dto.getContainsTagIds()) && CollectionUtils.isEmpty(dto.getExcludeTagIds())) {
            throw new BaseException("标签不能为空");
        }
    }

    /**
     * 人群名称校验
     */
    public static boolean check(String s) {
        return s != null && s.length() <= 10 && s.matches("[\\u4E00-\\u9FA5a-zA-Z0-9]+");
    }


    /**
     * 校验规则：非空、以英文字母开头（a-z/A-Z）
     */
    public static boolean checkEng(String s) {
        // 1. 排除空指针和空字符串（否则无法取首字符）
        if (s == null || s.isEmpty()) {
            return false;
        }
        // 2. 取字符串第一个字符
        char firstChar = s.charAt(0);
        // 3. 判断是否是英文字母（a-z或A-Z）
        return (firstChar >= 'a' && firstChar <= 'z') || (firstChar >= 'A' && firstChar <= 'Z');
    }

    public static void main(String[] args) {
        ArrayList<Integer> strings = new ArrayList<>();
        strings.add(1);
//        strings.add(2);
//        strings.add(3);

        String collect = strings.stream().map(String::valueOf).collect(Collectors.joining(","));
        System.out.println(collect);

    }
}
